package com.ws.common.util;


import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Created by WUSONG on 2018/2/27.
 */
public class LoadingCacheUtils {

    private static final long MAX_SIZE = 1000L;

    private LoadingCache<String,String> loadingCache;

    private Map<String,String> cacheMap = Maps.newHashMap();

    private Map<Class,Set<String>> fieldMap = Maps.newHashMap();

    private LoadingCacheUtils(){
        init();
    }

    private static class CacheHolder{
        private static final LoadingCacheUtils INSTANCE = new LoadingCacheUtils();
    }

    public static LoadingCacheUtils getInstance(){
        return CacheHolder.INSTANCE;
    }

    private void init(){
        loadingCache = CacheBuilder.newBuilder()
                .maximumSize(MAX_SIZE)
                .expireAfterAccess(1, TimeUnit.DAYS)
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        return cacheMap.get(key);
                    }
                });
    }

    public <T> void save(String key, T obj){
        Set<String> keys;
        if (fieldMap.containsKey(obj.getClass())){
            keys =fieldMap.get(obj.getClass());
            keys.add(key);
            fieldMap.put(obj.getClass(),keys);

        }else {
            keys = new HashSet<>();
            keys.add(key);
            fieldMap.put(obj.getClass(),keys);
        }
        cacheMap.put(key, JSONObject.toJSONString(obj));
        init();
    }

    public <T> T get(String key, Class<T> clazz) throws ExecutionException {
        String str = loadingCache.get(key);
        return JSONObject.parseObject(str,clazz);
    }

    public <T> List<T> getAll(Class<T> clazz) throws ExecutionException {
        List<T> objList = Lists.newArrayList();
        if (fieldMap.containsKey(clazz)){
            Set<String> set = fieldMap.get(clazz);
            Iterator<String> iterator = set.iterator();
            while (iterator.hasNext()){
                String setKey = iterator.next();
                if (cacheMap.containsKey(setKey)){
                    objList.add(this.get(setKey,clazz));
                }else {
                    iterator.remove();
                }
            }
        }
        return objList;
    }

    public void deleteAll(){
        fieldMap.clear();
        cacheMap.clear();
        refresh();

    }

    public <T> void deleteAll(Class<T> clazz){
        Set<String> set = Sets.newHashSet();
        if (fieldMap.containsKey(clazz)){
            set = fieldMap.get(clazz);
        }
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            cacheMap.remove(iterator.next());
        }
        refresh();
    }

    public void delete(Object key){
        if(cacheMap.containsKey(key)){
            cacheMap.remove(key);
        }
        refresh();
    }

    public void refresh(){
        loadingCache.invalidateAll();
        loadingCache.putAll(cacheMap);
    }

    public static void main(String[] args) throws ExecutionException {
        LoadingCacheUtils loadingCacheUtils = LoadingCacheUtils.getInstance();
        Test test = new Test();
        test.setName("Jack");
        loadingCacheUtils.save("A",test);
        System.out.println(loadingCacheUtils.get("A",Test.class).getName());
        List<Test> list = loadingCacheUtils.getAll(Test.class);
        for (Test test1 : list) {
            System.out.println(test1.getName());
        }
    }

    static class Test{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}